package org.anddev.andengine.entity.particle;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

import javax.microedition.khronos.opengles.GL10;

import org.anddev.andengine.engine.camera.Camera;
import org.anddev.andengine.entity.Entity;
import org.anddev.andengine.opengl.util.GLHelper;
import org.anddev.andengine.util.MathUtils;

/*
 * A simple & speedy 'pixel' particle system
 * 
 * 
 * >>>>> NOT WORKING PROPERLY YET!!! <<<<<
 *
 */

public class QuickParticleSystem extends Entity {

	public int mBoundsWidth = 480;
	public int mBoundsHeight = 320;
	public int mParticleMaxLife = 120;
	public int mParticleMinLife = 40;
	private float mParticleMaxSpeed = 15f;
	private float mParticleMinSpeed = 5f;
	public float mParticleDecay = 0.97f;
	public int mParticleFade = 20;
	public float mParticleFadeDecay = 0.7f;	

	private int mMaxParticles = 400;
	private int mSlotcount = 0;

	private final QuickParticleEntity[] mParticleArray = new QuickParticleEntity[mMaxParticles];
	private final QuickParticlePixel mPixel;
	private int mTotalActive;
	
	public QuickParticleSystem() {
		mPixel = new QuickParticlePixel();
		int t = 0;
		do {				
			mParticleArray[t] = new QuickParticleEntity();
			t++;
		} while (t < mMaxParticles);
		mSlotcount = 0;				
	}
	
	@Override
	public void onManagedUpdate(float pSecondsElapsed) {
		for(final QuickParticleEntity p : mParticleArray) {
			if( p.active > 0 ) {
				p.update();
			}
		}
	}

	@Override
	protected void onManagedDraw(GL10 pGL, Camera pCamera) {
		GLHelper.disableTextures(pGL);
		GLHelper.disableTexCoordArray(pGL);
		GLHelper.enableVertexArray(pGL);
		GLHelper.blendFunction(pGL, GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA);

		//render live particles
		mTotalActive = 0;
		for(final QuickParticleEntity p : mParticleArray) {
			if( p.active > 0 ) {
				//p.update();
				pGL.glPushMatrix();
				pGL.glColor4f(p.r, p.g, p.b, 1);
				pGL.glTranslatef(p.x, p.y, 0);
				mPixel.draw(pGL);
				pGL.glPopMatrix();
				pGL.glColor4f(1, 1, 1, 1);
				mTotalActive++;
			}
		}		
	}
	
	@Override
	public void reset() {
		for (QuickParticleEntity p : mParticleArray) {
			p.reset();
		}
		mSlotcount = 0;
	}

	
	/*
	 * 
	 * Public particle creator methods...
	 * 
	 */
	
	
	/*
	 * create a white particle at x,y with random direction
	 * good for explosions etc
	 */
	public void createParticle(float x, float y) {
		createParticle (x,y,1,1,1,MathUtils.random(0, 359),1,0f);
	}
	
	/*
	 * create an r,g,b colored particle at x,y with random direction
	 * good for colored explosions etc
	 */	
	public QuickParticleEntity createParticle(float x, float y, float r, float g, float b) {
		return createParticle (x,y,r,g,b,MathUtils.random(0, 359),1,0f);
	}

	/*
	 * create an r,g,b colored particle at x,y with a given direction
	 * good for directional streams, fire etc
	 */	
	public QuickParticleEntity createParticle(float x, float y, float r, float g, float b, float rotation) {
		return createParticle(x,y,r,g,b,rotation,1,0f);
	}

	/*
	 * create an r,g,b colored particle at x,y with a direction of random(360/divisions) + rotation
	 * good for stars, pinwheels etc
	 */	
	public QuickParticleEntity createParticle(float x, float y, float r, float g, float b, float rotation, int divisions) {
		return createParticle(x,y,r,g,b,rotation,divisions,0f);
	}

	/*
	 * create an r,g,b colored particle at x,y with a direction of random(360/divisions) + rotation, with an initial offset of startScale
	 * good for stars, pinwheels etc with a hole in the middle
	 */	
	public QuickParticleEntity createParticle(float x, float y, float r, float g, float b, float rotation, int divisions, float startScale) {		
		double dir;	
		final QuickParticleEntity p = mParticleArray[mSlotcount];
		p.x = x;
		p.y = y;
		p.r = r;
		p.g = g;
		p.b = b;
		p.active = MathUtils.random(mParticleMinLife,mParticleMaxLife);
		
		if (divisions > 1) {
			final float divs = 360f/divisions;
			final int segment = MathUtils.random(0,divisions-1);
			dir = Math.toRadians((divs * segment) + rotation);
		} else {
			dir = Math.toRadians(rotation);
		}
		
		final double mag = MathUtils.random(mParticleMinSpeed,mParticleMaxSpeed);
		p.dx =  (Math.cos(dir)*mag);
		p.dy =  (Math.sin(dir)*mag);
		
		if (startScale!=0) {
			p.x += p.dx*startScale;
			p.y += p.dy*startScale;		
		}
		
		mSlotcount ++;
		if (mSlotcount > mMaxParticles-1) mSlotcount = 0;
		
		return p;
	}
	
	/*
	 * create an explosion at x,y of numParticles density with random colors
	 */
	public void createExplosion(float x, float y, int numParticles) {
		if (numParticles<1) return;
		final float r = MathUtils.random(0.1f,1f);
		final float g = MathUtils.random(0.1f,1f);
		final float b = MathUtils.random(0.1f,1f);			
		do {
			  createParticle(x, y, r, g, b);
			  numParticles --;
		} while (numParticles>0);
	}		

	/*
	 * create an explosion at x,y of numParticles density with given color
	 */
	public void createExplosion(float x, float y, int numParticles, float r, float g, float b) {
		if (numParticles<1) return;
		do {
			  createParticle(x, y, r, g, b);
			  numParticles --;
		} while (numParticles>0);
	}		

	
	/*
	 * INNER CLASSES
	 */
	
	
	/*
	 * A simple non visual particle entity
	 */
	protected class QuickParticleEntity {
		float x = 0, y = 0;
		double dx = 0, dy = 0;
		public float r = 0, g = 0, b = 0;
		public int active = 0;
		
		private void update() {
			if (active > 0) {
				x += dx;
				y += dy;
				if ((x < dx) || (x > mBoundsWidth-1-dx)) {
					dx = -dx;
					x += dx*2;
				}
				if(( y < dy)  || (y > mBoundsHeight-1-dy)) {
					dy = -dy;
					y += dy*2;			
				}
				dx *= mParticleDecay;
				dy *= mParticleDecay;
				active --;
				//simple fading at end of life
				if (active < mParticleFade) {
					if (active < mParticleFade/2) {
						r *= mParticleFadeDecay * 2;
						g *= mParticleFadeDecay * 2;
						b *= mParticleFadeDecay * 2;											
					} else {
						r *= mParticleFadeDecay;
						g *= mParticleFadeDecay;
						b *= mParticleFadeDecay;				
					}
				}
			}
		}

		public void reset() {
			x = 0;
			y = 0;
			dx = 0;
			dy = 0;
			r = 0;
			g = 0;
			b = 0;
			active = 0;			
		}
	}
	
	/*
	 * A 'pixel' square used for drawing
	 */
	protected class QuickParticlePixel {
		// Our vertices.
		private final float vertices[] = {
			      -1.0f,  1.0f, 0.0f,  // 0, Top Left
			      -1.0f, -1.0f, 0.0f,  // 1, Bottom Left
			       1.0f, -1.0f, 0.0f,  // 2, Bottom Right
			       1.0f,  1.0f, 0.0f,  // 3, Top Right
			};
		private final int numVertices = vertices.length;
		
		// The order we like to connect them.
		//private short[] indices = { 0, 1, 2, 0, 2, 3 };
		//hmm, this winding seems the wrong way, but it works :-/
		private final short[] indices = { 0, 2, 1, 0, 3, 2 };
		private final int numIndices = indices.length;

		// Our vertex buffer.
		private final FloatBuffer vertexBuffer;
		// Our index buffer.
		private final ShortBuffer indexBuffer;

		public QuickParticlePixel() {
			// a float is 4 bytes, therefore we multiply the number if
			// vertices by 4.
			final ByteBuffer vbb = ByteBuffer.allocateDirect(numVertices * 4);
			vbb.order(ByteOrder.nativeOrder());
			vertexBuffer = vbb.asFloatBuffer();
			vertexBuffer.put(vertices);
			vertexBuffer.position(0);

			// short is 2 bytes, therefore we multiply the number if
			// vertices by 2.
			final ByteBuffer ibb = ByteBuffer.allocateDirect(numIndices * 2);
			ibb.order(ByteOrder.nativeOrder());
			indexBuffer = ibb.asShortBuffer();
			indexBuffer.put(indices);
			indexBuffer.position(0);
		}

		/**
		 * This function draws our square on screen.
		 * @param gl
		 */
		public void draw(GL10 gl) {
			gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
			gl.glDrawElements(GL10.GL_TRIANGLES, numIndices, GL10.GL_UNSIGNED_SHORT, indexBuffer);
		}

	}

}
